using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using System.Threading;
using System.IO;
using System.Runtime.InteropServices;
using Musicman.Client.Utilities;

namespace Musicman.Client
{
    public partial class CopyForm : Form
    {
        private Queue<CopyEntity> copyQueue;
        private object locker = new object();
        private volatile bool coping = false;
        private Musicman.Client.FileManagerService.FileManagerService fmService;
        private Musicman.Client.FileManagerService.FileDownloadHeader fdHeader;
        private AutomaticDownloader automaticDownloader;
        private long personID = 0;

        public CopyForm(Musicman.Client.FileManagerService.FileManagerService fileManagerService, AutomaticDownloader automaticDownloader, long personID)
        {
            InitializeComponent();
            this.fmService = fileManagerService;
            this.fdHeader = new Musicman.Client.FileManagerService.FileDownloadHeader();
            copyQueue = new Queue<CopyEntity>();
            this.automaticDownloader = automaticDownloader;
            this.personID = personID;
            this.listBox1.DataSource = copyQueue.ToArray();
            this.listBox1.DisplayMember = "SourceFileName";
        }

        #region Form Dragging API Support
        //The SendMessage function sends a message to a window or windows.

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]

        static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);

        //ReleaseCapture releases a mouse capture

        [DllImportAttribute("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]

        public static extern bool ReleaseCapture();

        private void CopyForm_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                ReleaseCapture();
                SendMessage(this.Handle, 0xa1, 0x2, 0);
            }
        }        
        #endregion


        public void AddFileToQueue(CopyEntity entity)
        {
            lock (locker)
            {
                this.copyQueue.Enqueue(entity);
                this.listBox1.DataSource = copyQueue.ToArray();
                this.listBox1.DisplayMember = "SourceFileName";
            }
            if (!coping)
            {
                coping = true;
                this.fancyProgress1.MaxOveralValue = 0;
                this.fancyProgress1.OveralValue = 0;
                ThreadPool.QueueUserWorkItem(new WaitCallback(CopyFiles));
            }
            this.fancyProgress1.MaxOveralValue += entity.FileLength;
        }

        private void CopyFiles(object o)
        {
            bool resume = true;

            while (resume)
            {
                int count = 0;
                CopyEntity entity = null;
                lock (locker)
                {
                    entity = copyQueue.Dequeue();
                    listBox1.DataSource = copyQueue.ToArray();
                    this.listBox1.DisplayMember = "SourceFileName";
                    count = copyQueue.Count;
                }
                this.fancyProgress1.CurrentWorking = String.Format("{0} files remain", count+1);
                this.Text = this.fancyProgress1.CurrentWorking;
                CopyOneFile(entity);
                lock (locker)
                {
                    resume = copyQueue.Count != 0;
                }
            }
            this.fancyProgress1.CurrentWorking = String.Format("{0} files remain", 0);
            this.Text = this.fancyProgress1.CurrentWorking;
            this.fancyProgress1.SingleValue += 0;
            this.fancyProgress1.OveralValue = fancyProgress1.MaxOveralValue;
            coping = false;
            this.Hide();
        }

        private void CopyOneFile(CopyEntity entity)
        {
            string destName = String.Format("{0}\\{1}", entity.CopyDestination, entity.SourceFileName);
            FileStream destFs = null;
            FileStream sourceFs = null;
            this.fancyProgress1.MaxSingleValue = entity.FileLength;
            this.fancyProgress1.SingleValue = 0;

            const int buffersize = 1024 * 16;
            byte[] buffer = new byte[buffersize];
            try
            {
                destFs = File.Open(destName, FileMode.Create, FileAccess.Write);
                if (File.Exists(entity.SourcePath))
                {
                    sourceFs = File.Open(entity.SourcePath, FileMode.Open, FileAccess.Read);
                    int count = sourceFs.Read(buffer, 0, buffersize);
                    while (count > 0)
                    {
                        destFs.Write(buffer, 0, count);
                        count = sourceFs.Read(buffer, 0, buffersize);
                        this.fancyProgress1.SingleValue += count;
                        this.fancyProgress1.OveralValue += count;
                    }
                    sourceFs.Close();
                    this.fmService.UpdateDownloadStats(this.personID, entity.FileID, 1);
                    destFs.Close();
                }
                else // we are not in adequate local network, try to get file via web service
                {
                    this.fdHeader.exists = false;
                    fmService.FileDownloadHeaderValue = fdHeader;
                    string oldText = this.fancyProgress1.CurrentWorking;
                    this.fancyProgress1.CurrentWorking = "...retreiving...";
                    this.Text = this.fancyProgress1.CurrentWorking;
                    byte[] fileContent = new byte[0];
                    if (entity.FileID != 0)
                    {
                        fileContent = fmService.DownloadFile(this.personID, entity.FileID);
                    }
                    else
                    {
                        fileContent = fmService.DownloadFilePath(this.personID, entity.SourcePath);
                    }
                    this.fancyProgress1.CurrentWorking = oldText;
                    if (fmService.FileDownloadHeaderValue.exists)
                    {
                        int length = 0;
                        for (int i = 0; i < fileContent.Length / buffersize; i++)
                        {
                            length = (i + 1) * buffersize > fileContent.Length ? fileContent.Length % buffersize : buffersize;
                            destFs.Write(fileContent, i * buffersize, length);
                            this.fancyProgress1.SingleValue += length;
                            this.fancyProgress1.OveralValue += length;
                        }
                    }
                    else
                    {
                        destFs.Close();
                        File.Delete(destFs.Name);
                        if (automaticDownloader.Settings.UseDownloader)
                        {
                            AutomaticDownloadEntity automaticEntity = new AutomaticDownloadEntity(entity, personID);
                            automaticDownloader.AddDownloadEntityToQueue(automaticEntity);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                string message = ex.Message;
                string innerMessage = (ex.InnerException == null) ? "" : ex.InnerException.Message;
                fmService.LogClientException(this.personID, "Error copying file", message, innerMessage);
            }
            finally
            {
                if (destFs != null) destFs.Close();
                if (sourceFs != null) sourceFs.Close();
            }
        }

        private void LogLocally(string message)
        {
            FileStream fs = File.Open("C:\\ClientInfo.txt", FileMode.Append, FileAccess.Write);
            StreamWriter sw = new StreamWriter(fs);
            sw.WriteLine(message);
            sw.Close();
            fs.Close();
        }

        private void btnBackground_Click(object sender, EventArgs e)
        {
            this.Hide();
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            this.Hide();
            lock (locker)
            {
                this.copyQueue.Clear();
            }
        }

        private void CopyForm_Load(object sender, EventArgs e)
        {
            System.Drawing.Drawing2D.GraphicsPath myPath = new System.Drawing.Drawing2D.GraphicsPath();
            myPath.AddArc(20, this.Height * 1 / 10, this.Width / 2, this.Height * 4 / 5, 60, 240);
            myPath.AddArc(this.Width / 2 - 20, this.Height * 1 / 10, this.Width / 2, this.Height * 4 / 5, 240, 240);
            Region myRegion = new Region(myPath);
            this.Region = myRegion;
        }

        private void CopyForm_Paint(object sender, PaintEventArgs e)
        {
            Graphics gr = e.Graphics;
            SolidBrush sb = new SolidBrush(Color.Black);
            Pen p = new Pen(sb);
            p.Width = 4;
            gr.DrawArc(p, 20, this.Height * 1/ 10, this.Width / 2, this.Height * 4/5 , 60, 240);
            gr.DrawArc(p, this.Width / 2 - 20, this.Height * 1 / 10, this.Width / 2, this.Height * 4 / 5, 240, 240);
        }


    }

    public class CopyEntity
    {
        private string copyDestination;
        private string sourcePath;
        private string sourceFileName;
        private long fileLength;
        private long fileID;

        public string CopyDestination
        {
            get { return copyDestination; }
            set { copyDestination = value; }
        }

        public string SourcePath
        {
            get { return sourcePath; }
            set { sourcePath = value; }
        }

        public string SourceFileName
        {
            get { return sourceFileName; }
            set { sourceFileName = value; }
        }

        public long FileLength
        {
            get { return fileLength; }
            set { fileLength = value; }
        }

        public long FileID
        {
            get { return fileID; }
            set { fileID = value; }
        }
    }
}
